/*

//-----------------------------------
ILI9341 STM32的GFX库
//-----------------------------------
//
非常简单的 GFX 库建立在ILI9342_STM32_Driver库之上。
向 ILI9341 添加基本形状、图像和字体绘制功能
//
库是为 STM32 HAL 库编写的，支持 STM32CUBEMX。将库与 Cube 软件一起使用
您需要勾选在各自的 .c 和 .h 文件中生成外设初始化代码的框
//
//
//-----------------------------------
如何使用此库
//-----------------------------------
//
- 如果使用 STM32F7 以外的 MCU，则必须将 ILI9341_GFX.h 中的 #include“stm32f7xx_hal.h”更改为相应的 .h 文件
//
如果使用“ILI9341_STM32_Driver”，则已满足使用库的所有其他先决条件
只需包含该库，它就可以使用了
//
//-----------------------------------
*/


#include "ILI9341_STM32_Driver.h"
#include "ILI9341_GFX.h"
#include "5x5_font.h"
#include "spi.h"

/*Draw hollow circle at X,Y location with specified radius and colour. X and Y represent circles center */
/**
 * @brief 绘制空心圆
 *
 * 在ILI9341屏幕上绘制一个指定中心坐标、半径和颜色的空心圆。
 *
 * @param X 圆心X坐标
 * @param Y 圆心Y坐标
 * @param Radius 圆的半径
 * @param Colour 圆的颜色
 */
void ILI9341_Draw_Hollow_Circle(uint16_t X, uint16_t Y, uint16_t Radius, uint16_t Colour)
{
	int x = Radius-1;
    int y = 0;
    int dx = 1;
    int dy = 1;
    int err = dx - (Radius << 1);

    while (x >= y)
    {
        ILI9341_Draw_Pixel(X + x, Y + y, Colour);
        ILI9341_Draw_Pixel(X + y, Y + x, Colour);
        ILI9341_Draw_Pixel(X - y, Y + x, Colour);
        ILI9341_Draw_Pixel(X - x, Y + y, Colour);
        ILI9341_Draw_Pixel(X - x, Y - y, Colour);
        ILI9341_Draw_Pixel(X - y, Y - x, Colour);
        ILI9341_Draw_Pixel(X + y, Y - x, Colour);
        ILI9341_Draw_Pixel(X + x, Y - y, Colour);

        if (err <= 0)
        {
            y++;
            err += dy;
            dy += 2;
        }
        if (err > 0)
        {
            x--;
            dx += 2;
            err += (-Radius << 1) + dx;
        }
    }
}

/*Draw filled circle at X,Y location with specified radius and colour. X and Y represent circles center */
/**
 * @brief 绘制填充圆
 *
 * 在ILI9341屏幕上绘制一个填充的圆形。
 *
 * @param X 圆心X坐标
 * @param Y 圆心Y坐标
 * @param Radius 圆的半径
 * @param Colour 圆的颜色
 */
void ILI9341_Draw_Filled_Circle(uint16_t X, uint16_t Y, uint16_t Radius, uint16_t Colour)
{
	
		int x = Radius;
    int y = 0;
    int xChange = 1 - (Radius << 1);
    int yChange = 0;
    int radiusError = 0;

    while (x >= y)
    {
        for (int i = X - x; i <= X + x; i++)
        {
            ILI9341_Draw_Pixel(i, Y + y,Colour);
            ILI9341_Draw_Pixel(i, Y - y,Colour);
        }
        for (int i = X - y; i <= X + y; i++)
        {
            ILI9341_Draw_Pixel(i, Y + x,Colour);
            ILI9341_Draw_Pixel(i, Y - x,Colour);
        }

        y++;
        radiusError += yChange;
        yChange += 2;
        if (((radiusError << 1) + xChange) > 0)
        {
            x--;
            radiusError += xChange;
            xChange += 2;
        }
    }
		//Really slow implementation, will require future overhaul
		//TODO:	https://stackoverflow.com/questions/1201200/fast-algorithm-for-drawing-filled-circles	
}

/*Draw a hollow rectangle between positions X0,Y0 and X1,Y1 with specified colour*/
/**
 * @brief 绘制空心的矩形（通过坐标）
 *
 * 使用给定的坐标和颜色，在ILI9341屏幕上绘制一个空心的矩形。
 *
 * @param X0 矩形左上角的X坐标
 * @param Y0 矩形左上角的Y坐标
 * @param X1 矩形右下角的X坐标
 * @param Y1 矩形右下角的Y坐标
 * @param Colour 矩形的颜色
 */
void ILI9341_Draw_Hollow_Rectangle_Coord(uint16_t X0, uint16_t Y0, uint16_t X1, uint16_t Y1, uint16_t Colour)
{
	uint16_t 	X_length = 0;
	uint16_t 	Y_length = 0;
	uint8_t		Negative_X = 0;
	uint8_t 	Negative_Y = 0;
	float 		Calc_Negative = 0;
	
	Calc_Negative = X1 - X0;
	if(Calc_Negative < 0) Negative_X = 1;
	Calc_Negative = 0;
	
	Calc_Negative = Y1 - Y0;
	if(Calc_Negative < 0) Negative_Y = 1;
	
	
	//DRAW HORIZONTAL!
	if(!Negative_X)
	{
		X_length = X1 - X0;		
	}
	else
	{
		X_length = X0 - X1;		
	}
	ILI9341_Draw_Horizontal_Line(X0, Y0, X_length, Colour);
	ILI9341_Draw_Horizontal_Line(X0, Y1, X_length, Colour);
	
	
	
	//DRAW VERTICAL!
	if(!Negative_Y)
	{
		Y_length = Y1 - Y0;		
	}
	else
	{
		Y_length = Y0 - Y1;		
	}
	ILI9341_Draw_Vertical_Line(X0, Y0, Y_length, Colour);
	ILI9341_Draw_Vertical_Line(X1, Y0, Y_length, Colour);
	
	if((X_length > 0)||(Y_length > 0)) 
	{
		ILI9341_Draw_Pixel(X1, Y1, Colour);
	}
	
}

/*Draw a filled rectangle between positions X0,Y0 and X1,Y1 with specified colour*/
/**
 * @brief 在指定坐标上绘制填充矩形
 *
 * 在给定的坐标范围内，绘制一个填充指定颜色的矩形。
 *
 * @param X0 矩形左上角的X坐标
 * @param Y0 矩形左上角的Y坐标
 * @param X1 矩形右下角的X坐标
 * @param Y1 矩形右下角的Y坐标
 * @param Colour 矩形的填充颜色
 */
void ILI9341_Draw_Filled_Rectangle_Coord(uint16_t X0, uint16_t Y0, uint16_t X1, uint16_t Y1, uint16_t Colour)
{
	uint16_t 	X_length = 0;
	uint16_t 	Y_length = 0;
	uint8_t		Negative_X = 0;
	uint8_t 	Negative_Y = 0;
	int32_t 	Calc_Negative = 0;
	
	uint16_t X0_true = 0;
	uint16_t Y0_true = 0;
	
	Calc_Negative = X1 - X0;
	if(Calc_Negative < 0) Negative_X = 1;
	Calc_Negative = 0;
	
	Calc_Negative = Y1 - Y0;
	if(Calc_Negative < 0) Negative_Y = 1;
	
	
	//DRAW HORIZONTAL!
	if(!Negative_X)
	{
		X_length = X1 - X0;
		X0_true = X0;
	}
	else
	{
		X_length = X0 - X1;
		X0_true = X1;
	}
	
	//DRAW VERTICAL!
	if(!Negative_Y)
	{
		Y_length = Y1 - Y0;
		Y0_true = Y0;		
	}
	else
	{
		Y_length = Y0 - Y1;
		Y0_true = Y1;	
	}
	
	ILI9341_Draw_Rectangle(X0_true, Y0_true, X_length, Y_length, Colour);	
}




/**
 * @brief 绘制字符
 *
 * 在ILI9341显示屏上绘制一个字符。
 *
 * @param Character 要绘制的字符
 * @param X 字符的X坐标
 * @param Y 字符的Y坐标
 * @param Colour 字符的颜色
 * @param Size 字符的大小
 * @param Background_Colour 字符背景的颜色
 */
void ILI9341_Draw_Char(char Character, uint8_t X, uint8_t Y, uint16_t Colour, uint16_t Size, uint16_t Background_Colour) 
{
    uint8_t i, j;
    // 判断字符是否小于空格，如果是则设为空格
    char function_char = (Character < ' ') ? ' ' : Character;

    // 如果字符不是空格，则减去32（针对ASCII码的偏移）
    if (function_char >= ' ') {
        function_char -= 32;
    }

    // 绘制字符背景矩形
    ILI9341_Draw_Rectangle(X, Y, CHAR_WIDTH * Size, CHAR_HEIGHT * Size, Background_Colour);

    // 遍历字符的每一列
    for (j = 0; j < CHAR_WIDTH; j++) {
        // 遍历字符的每一行
        for (i = 0; i < CHAR_HEIGHT; i++) {
            // 判断当前位是否为1
            if (font[function_char][j] & (1 << i)) {
                // 根据Size绘制像素或矩形
                if (Size == 1) {
                    ILI9341_Draw_Pixel(X + j, Y + i, Colour);
                } else {
                    ILI9341_Draw_Rectangle(X + (j * Size), Y + (i * Size), Size, Size, Colour);
                }
            }
        }
    }
}
/**
 * @brief 绘制汉字
 *
 * 在指定的位置绘制一个汉字，使用给定的颜色和大小。
 *
 * @param hanzi_index 汉字索引
 * @param X 绘制位置的X坐标
 * @param Y 绘制位置的Y坐标
 * @param Colour 汉字颜色
 * @param Size 汉字大小
 * @param Background_Colour 背景颜色
 */
void ILI9341_Draw_Hanzi(uint8_t hanzi_index, uint16_t X, uint16_t Y, uint16_t Colour, uint16_t Size, uint16_t Background_Colour) 
{
    // 检查汉字索引是否有效
    if (hanzi_index >= sizeof(hanzi_bitmap) / (HANZI_WIDTH * HANZI_HEIGHT / 8)) {
        return; // 无效的汉字索引
    }

    // 绘制背景矩形
    // Draw background rectangle
    ILI9341_Draw_Rectangle(X, Y, HANZI_WIDTH * Size, HANZI_HEIGHT * Size, Background_Colour);

    // 优化汉字字符的绘制逻辑
    // Optimize drawing logic for Hanzi character
    uint16_t i, j, k;
    uint16_t byte_index = 0;
    uint16_t current_byte;
    const uint8_t* hanzi_ptr = hanzi_bitmap[hanzi_index];

    for (j = 0; j < HANZI_HEIGHT; j++) {
        for (i = 0; i < HANZI_WIDTH / 8; i++) {
            current_byte = hanzi_ptr[byte_index++];
            for (k = 0; k < 8; k++) {
                // 如果当前位为1，则绘制一个矩形
                if (current_byte & (1 << k)) {
                    ILI9341_Draw_Rectangle(X + ((i * 8 + k) * Size), Y + (j * Size), Size, Size, Colour);
                }
            }
        }
    }
}


/**
 * @brief 绘制汉字字符串
 *
 * 在指定的位置，使用指定的颜色、大小和背景色绘制汉字字符串。
 *
 * @param hanzi_string 汉字字符串指针
 * @param X 绘制起始位置的X坐标
 * @param Y 绘制起始位置的Y坐标
 * @param Colour 绘制汉字的颜色
 * @param Size 绘制汉字的大小
 * @param Background_Colour 绘制汉字的背景色
 */
void ILI9341_Draw_Hanzi_String(const char* hanzi_string, uint8_t X, uint8_t Y, uint16_t Colour, uint16_t Size, uint16_t Background_Colour) 
{
    // 遍历汉字字符串，逐个绘制
    while (*hanzi_string) {
        ILI9341_Draw_Hanzi(*hanzi_string++, X, Y, Colour, Size, Background_Colour);
        // 更新下一个汉字的起始X坐标
        X += HANZI_WIDTH * Size;
    }
}







/*Draws an array of characters (fonts imported from fonts.h) at X,Y location with specified font colour, size and Background colour*/
/*See fonts.h implementation of font on what is required for changing to a different font when switching fonts libraries*/
/**
 * @brief 在ILI9341屏幕上绘制文本
 *
 * 在ILI9341屏幕上绘制指定的文本内容。根据给定的坐标、颜色、字体大小和背景颜色绘制文本。
 *
 * @param Text 文本内容
 * @param X 文本起始的X坐标
 * @param Y 文本起始的Y坐标
 * @param Colour 文本颜色
 * @param Size 字体大小
 * @param Background_Colour 背景颜色
 */
void ILI9341_Draw_Text(const char* Text, uint8_t X, uint8_t Y, uint16_t Colour, uint16_t Size, uint16_t Background_Colour)
{
    while (*Text) {
        ILI9341_Draw_Char(*Text++, X, Y, Colour, Size, Background_Colour);
        X += CHAR_WIDTH*Size;
    }
}

/*Draws a full screen picture from flash. Image converted from RGB .jpeg/other to C array using online converter*/
//USING CONVERTER: http://www.digole.com/tools/PicturetoC_Hex_converter.php
//65K colour (2Bytes / Pixel)
/**
 * @brief 在ILI9341屏幕上绘制图像
 *
 * 根据给定的图像数组和屏幕方向，在ILI9341屏幕上绘制图像。
 *
 * @param Image_Array 图像数组指针
 * @param Orientation 屏幕方向（水平或垂直）
 */
void ILI9341_Draw_Image(const char* Image_Array, uint8_t Orientation)
{
	if(Orientation == SCREEN_HORIZONTAL_1)
	{
		ILI9341_Set_Rotation(SCREEN_HORIZONTAL_1);
		ILI9341_Set_Address(0,0,ILI9341_SCREEN_WIDTH,ILI9341_SCREEN_HEIGHT);
			
		LCD_DC_H();
		LCD_CS_L();
		
		unsigned char Temp_small_buffer[BURST_MAX_SIZE];
		uint32_t counter = 0;
		for(uint32_t i = 0; i < ILI9341_SCREEN_WIDTH*ILI9341_SCREEN_HEIGHT*2/BURST_MAX_SIZE; i++)
		{			
				for(uint32_t k = 0; k< BURST_MAX_SIZE; k++)
				{
					Temp_small_buffer[k]	= Image_Array[counter+k];		
				}						
				HAL_SPI_Transmit(&hspi1, (unsigned char*)Temp_small_buffer, BURST_MAX_SIZE, 10);	
				counter += BURST_MAX_SIZE;			
		}
		LCD_CS_H();
	}
	else if(Orientation == SCREEN_HORIZONTAL_2)
	{
		ILI9341_Set_Rotation(SCREEN_HORIZONTAL_2);
		ILI9341_Set_Address(0,0,ILI9341_SCREEN_WIDTH,ILI9341_SCREEN_HEIGHT);
			
		LCD_DC_H();
		LCD_CS_L();
		
		unsigned char Temp_small_buffer[BURST_MAX_SIZE];
		uint32_t counter = 0;
		for(uint32_t i = 0; i < ILI9341_SCREEN_WIDTH*ILI9341_SCREEN_HEIGHT*2/BURST_MAX_SIZE; i++)
		{			
				for(uint32_t k = 0; k< BURST_MAX_SIZE; k++)
				{
					Temp_small_buffer[k]	= Image_Array[counter+k];		
				}						
				HAL_SPI_Transmit(&hspi1, (unsigned char*)Temp_small_buffer, BURST_MAX_SIZE, 10);	
				counter += BURST_MAX_SIZE;			
		}
		LCD_CS_H();
	}
	else if(Orientation == SCREEN_VERTICAL_2)
	{
		ILI9341_Set_Rotation(SCREEN_VERTICAL_2);
		ILI9341_Set_Address(0,0,ILI9341_SCREEN_HEIGHT,ILI9341_SCREEN_WIDTH);
			
		LCD_DC_H();
		LCD_CS_L();
		
		unsigned char Temp_small_buffer[BURST_MAX_SIZE];
		uint32_t counter = 0;
		for(uint32_t i = 0; i < ILI9341_SCREEN_WIDTH*ILI9341_SCREEN_HEIGHT*2/BURST_MAX_SIZE; i++)
		{			
				for(uint32_t k = 0; k< BURST_MAX_SIZE; k++)
				{
					Temp_small_buffer[k]	= Image_Array[counter+k];		
				}						
				HAL_SPI_Transmit(&hspi1, (unsigned char*)Temp_small_buffer, BURST_MAX_SIZE, 10);	
				counter += BURST_MAX_SIZE;			
		}
		LCD_CS_H();
	}
	else if(Orientation == SCREEN_VERTICAL_1)
	{
		ILI9341_Set_Rotation(SCREEN_VERTICAL_1);
		ILI9341_Set_Address(0,0,ILI9341_SCREEN_HEIGHT,ILI9341_SCREEN_WIDTH);
			
		LCD_DC_H();
		LCD_CS_L();
		
		unsigned char Temp_small_buffer[BURST_MAX_SIZE];
		uint32_t counter = 0;
		for(uint32_t i = 0; i < ILI9341_SCREEN_WIDTH*ILI9341_SCREEN_HEIGHT*2/BURST_MAX_SIZE; i++)
		{			
				for(uint32_t k = 0; k< BURST_MAX_SIZE; k++)
				{
					Temp_small_buffer[k]	= Image_Array[counter+k];		
				}						
				HAL_SPI_Transmit(&hspi1, (unsigned char*)Temp_small_buffer, BURST_MAX_SIZE, 10);	
				counter += BURST_MAX_SIZE;			
		}
		LCD_CS_H();
	}
}


